/* 内核执行头程序的位置图
                |           |
                |           |
                |   Kernel  |
                |           |
      物理内存  |-----------|
                |   head.S  |   1 MB
    -------------------------------------
                |           |
                | BootLoader|
                |           |   0
    -------------------------------------

*/

// 内核执行头程序起始线性地址为0xffff800000000000 + 0x100000，暂时不清楚原因

#include "linkage.h"

.section .text                      // 代码段
.globl _start                       // globl类似c语言extern，_start在GAS编译器中表示程序默认起始位置
_start:
    mov $0x10, %ax                  // ax = 0x10，设置段寄存器为代码段选择子
    mov %ax, %ds                    // ds = ax
    mov %ax, %es                    // es = ax
    mov %ax, %fs                    // fs = ax
    mov %ax, %ss                    // ss = ax
    mov $0x7e00, %esp               // esp = 0x7e00，设置栈
    
// ==========   load GDTR
    lgdt GDT_POINTER(%rip)          // RIP-Relative寻址，intel汇编格式:[rip + displacement]，AT&T汇编格式：displacement(%rip)
                                    //      displacement是一个有符号32位整数值，目标地址依赖于当前的RIP指令寄存器，因此寻址范围为RIP±2GB
                                    //      displacement为常数时直接rip下一条指令地址偏移即可，为符号时表示的是符号的真实地址
// ==========   load IDTR
    lidt    IDT_POINTER(%rip)       // 同GDT

    movl $0x10, %eax                   // 设置GDT和IDT后需要重新设置段寄存器
    mov %ax, %ds
    mov %ax, %es
    mov %ax, %fs
    mov %ax, %gs
    mov %ax, %ss
    movq $0x7e00, %rsp

// ==========   load cr3
    movq $0x101000, %rax            // rax = 0x101000
    movq %rax, %cr3                 // cr3 = rax
    movq switch_seg(%rip), %rax     // rax = switch_seg地址中的值，也就是rax = entry64
    pushq $0x08                     // 段选择子入栈，段选择子为0x08，GDT中对应内核代码段
    pushq %rax                      // rax入栈
    lretq                           // 前面将段选择子和执行代码偏移地址入栈，调用lretq模拟函数返回，因为GAS不支持ljmp和lcall指令

// ==========   64-bit mode code
switch_seg:                         // 从线性地址0x100000跳转到0xffff800000100000
    .quad   entry64

entry64:
    movq $0x10, %rax                // 重新设置段寄存器
    movq %rax, %ds
    movq %rax, %es
    movq %rax, %gs
    movq %rax, %ss
    movq _stack_start(%rip), %rsp  // 设置64位栈空间

setup_IDT:
    leaq ignore_int(%rip), %rdx     // rdx = ignore_int，和load cr3过程中movq switch_seg(%rip), %rax不一样
    movq $(0x08 << 16), %rax        // rax = 0x08 << 16
    movw %dx, %ax                   // ax = dx，16位寄存器
    movq $(0x8e00 << 32), %rcx      // rcx = 0x8e00 << 32
    addq %rcx, %rax                 // rax = rax + rcx
    movl %edx, %ecx                 // ecx = edx，32位寄存器
    shrl $16, %ecx                  // ecx = ecx >> 16
    shlq $48, %rcx                  // rcx = rcx << 48
    addq %rcx, %rax                 // rax = rax + rcx
    shrq $32, %rdx                  // rdx = rdx >> 32，此时rax：[63:48]=ignore_int[31:16],[47:32]=0x8e00,[31:16]=0x08,[15:0]=ignore_int[15:0]，rdx=ignore_int[63:32]
    leaq IDT_Table(%rip), %rdi      // rdi = IDT_Table
    mov $256, %rcx                  // rcx = 256，rp_sidt执行256次
rp_sidt:
    movq %rax, (%rdi)               // rdi指向地址写入rax值
    movq %rdx, 8(%rdi)              // rdi+8指向地址写入rdx值
    addq $0x10, %rdi                // rdi = rdi + 16
    dec %rcx                        // rcx = rcx - 1
    jne rp_sidt                     // 如果ZF != 0即，rcx != 0，跳转到rp_sidt循环执行

setup_TSS64:
    leaq TSS64_Table(%rip), %rdx    // rdx = TSS64_Table
    xorq %rax, %rax                 // rax = 0
    xorq %rcx, %rcx                 // rcx = 0
    movq $0x89, %rax                // rax = 0x89
    shlq $40, %rax                  // rax = rax << 40
    movl %edx, %ecx                 // ecx = edx，32位寄存器
    shrl $24, %ecx                  // ecx = ecx >> 24
	shlq $56, %rcx                  // rcx = rcx << 56
	addq %rcx, %rax                 // rax = rax + rcx
	xorq %rcx, %rcx                 // rcx = 0
	movl %edx, %ecx                 // ecx = edx，32位寄存器
	andl $0xffffff,	%ecx            // ecx = ecx & 0xffffff
	shlq $16, %rcx                  // rcx = rcx << 16
	addq %rcx, %rax                 // rax = rax + rcx
	addq $103, %rax                 // rax = rax + 103，此时rax:[63:56]=TSS64_Table[31:24],[55:40]=0x89,[39:16]=TSS64_Table[23:0],[15:0]=103
	leaq GDT_Table(%rip), %rdi      // rdi = GDT_Table
	movq %rax, 80(%rdi)             // rdi + 80指向地址写入rax值
	shrq $32, %rdx                  // rdx = rdx >> 32
	movq %rdx, 88(%rdi)             // rdi + 88指向地址写入rdx
	// mov	$0x50, %ax                  // ax = 0x50，16位寄存器，注释后在Start_Kernel函数中设置
	// ltr	%ax                         // 设置当前任务寄存器                   

    movq go_to_kernel(%rip), %rax   // 模拟函数返回，跳转到Start_Kernel执行
    pushq $0x08
    pushq %rax
    lretq

go_to_kernel:
    .quad   Start_Kernel

// ==========   ignore_int

ignore_int:
	cld                             // 清除DF标志
	pushq %rax                      // 保存寄存器值到栈
	pushq %rbx
	pushq %rcx
	pushq %rdx
	pushq %rbp
	pushq %rdi
	pushq %rsi

	pushq %r8
	pushq %r9
	pushq %r10
	pushq %r11
	pushq %r12
	pushq %r13
	pushq %r14
	pushq %r15

	movq %es, %rax                  // rax = es
	pushq %rax                      // es的值入栈
	movq %ds, %rax                  // rax = ds
	pushq %rax                      // ds的值入栈

	movq $0x10,	%rax                // rax = 0x10，设置DS和ES段寄存器为内核数据段
	movq %rax, %ds                  // ds = rax
	movq %rax, %es                  // es = rax

	leaq int_msg(%rip), %rax			/* leaq get address */
	pushq %rax                      // 保存int_msg地址
	movq %rax, %rdx                 // rdx = rax
	movq $0x00000000, %rsi          // rsi = 0x00000000，color_printk参数，背景色，函数传参采用寄存器，从左到右依次使用RDI,RSI,RDX,RCX,R8,R9
	movq $0x00ff0000, %rdi          // rdi = 0x00ff0000，color_printk参数，前景色
	movq $0, %rax                   // rax = 0，rax存放函数的返回值
	callq color_printk              // 调用color_printk函数
	addq $0x8, %rsp                 // rsp = rsp + 8

Loop:
	jmp	Loop	                    // 进入死循环

	popq %rax
	movq %rax, %ds
	popq %rax
	movq %rax, %es

	popq %r15
	popq %r14
	popq %r13
	popq %r12
	popq %r11
	popq %r10
	popq %r9
	popq %r8

	popq %rsi
	popq %rdi
	popq %rbp
	popq %rdx
	popq %rcx
	popq %rbx
	popq %rax
	iretq

int_msg:
	.asciz "Unknown interrupt or fault at RIP\n"

ENTRY(_stack_start)
    .quad g_init_task_union + 32768

// ==========   init page
/*
64位系统其实寻址能力为48位，高16位置位和第47位一样，因此线性地址范围为0x0000 0000 0000 0000~0x0000 7fff ffff ffff和0xffff 8000 0000 0000~0xffff ffff ffff ffff
线性地址结构：
    63~48: 符号位
    47~39: PML4表索引，根据索引取得PML4表项，12~MAXPHYADDR-1，MAXPHYADDR取值36~52, 乘以0x1000得到下一级页表PDPT基地址 + 索引号*8得到下一级页表项地址，其它同理
    38~30: PDPT表索引，与PML4表类似，如果采用1G页大小，则采用二级页表，后面的都是页偏移地址
    29~21: PDT表索引，采用2MB或4KB页大小的时候才有，如果采用2MB页大小，则采用三级页表，后面21位都是页偏移地址，这里采用的就是2MB页大小
    20~12: PT表索引，采用4KB页大小的时候才有
    11~0: 页偏移地址，4KB页大小页偏移地址有12bit，2MB页大小的页偏移地址有21bit，1GB页大小的页偏移地址有30bit

寻址例子：
    应用程序中定义一个字符串"HelloWorld"，打印获得虚拟地址为0x2ffde8，CR3寄存器保存PML4表基地址为0x7d838000
    0x2ffde8 => 0000000000000000 000000000 000000000 000000001 011111111 110111101000
    sign: 0     PML4索引: 0x0   PDPT索引: 0x0   PDT索引: 0x1    PT索引: 0xff    页内偏移地址: 0xde8
    1.查找PML4表，0x7d838000 + 0x0 * 8 = 0x7d838000
    #7d838000 02b00000-7d274867 00000000-0000000
    2.得到PML4表项0x02b00000-7d274867，一般个人电脑MAXPHYADDR为36，[12:35] = 0x07d274，0x07d274 * 0x1000 + 0x0 * 8 = 0x07d274000得到PDPT表项地址
    #07d274000: 03000000-7d737867 01900000-7d23f867
    3.得到PDPT表项0x03000000-7d737867，PS.[7] = 0表示还有下一级页表(如果为1表示系统采用1GB页大小), [12:35] = 0x07d737，0x07d737 * 0x1000 + 0x1 * 8 = 0x07d737008得到PDT表项地址
    #07d737008: 01500000-7d7bb867 11700000-7b596867
    4.得到0x01500000-7d7bb867，PS.[7] = 0表示还有下一级页表(如果为1表示系统采用2MB页大小)，[12:15] = 0x07d7bb，0x07d7bb * 0x1000 + 0xff * 8 = 0x07d7bb7f8得到PT表项地址
    #07d7bb7f8: 89a00000-7d084867 97300000-7afbc867
    5.得到0x89a00000-7d084867，[12:15] = 0x07d084，0x07d084 * 0x1000 + 0xde8 = 0x07d084de8得到物理地址
    #07d084de8: 48 65 6c 6c 6f 57 6f 72-6c 64 00 00 00 00 00 00 HelloWorld......
*/
.align 8

.org    0x1000
__PML4E:
    .quad   0x102003
    .fill   255,8,0
    .quad   0x102003
    .fill   255,8,0

.org    0x2000
_PDPTE:
    .quad   0x103003
    .fill   511,8,0

.org    0x3000
__PDE:
    .quad   0x000083
    .quad   0x200083
    .quad   0x400083
    .quad   0x600083
    .quad   0x800083
    .quad   0xa00083
    .quad   0xc00083
    .quad   0xe00083
    .quad   0x1000083
    .quad   0x1200083
    .quad   0x1400083
    .quad   0x1600083
    .quad   0x1800083
    .quad   0x1a00083
    .quad   0x1c00083
    .quad   0x1e00083
    .quad   0x2000083
    .quad   0x2200083
    .quad   0x2400083
    .quad   0x2600083
    .quad   0x2800083
    .quad   0x2a00083
    .quad   0x2c00083
    .quad   0x2e00083
    .quad   0xe0000083                  /* 0xffff800003000000线性地址, PML4索引=0x100, PDPT索引=0x0, PDE索引=0x18, 找到这一页，offset=0x0，基地址=0xe0000，得到地址0xe0000000, vbe帧缓存地址 */
    .quad   0xe0200083
    .quad   0xe0400083
    .quad   0xe0600083
    .quad   0xe0800083
    .quad   0xe0a00083
    .quad   0xe0c00083
    .quad   0xe0e00083
    .fill   480,8,0

// ==========   GDT_Table

.section .data                      // 数据段
.globl GDT_Table
    /*
        代码段(Segment 08)解析
        0~15        段长度(15:0)            0x0000      IA-32e模式下忽略
        16~31       段基地址(15:0)          0x0000      IA-32e模式下忽略
        32~39       段基地址(23:16)         0x00        IA-32e模式下忽略
        40~43       Type区域                0x8         非一致性、不可读、未访问
        44          S标志                   0x1         代码段
        45~46       DPL                     0x00        0特权级
        47          P                       0x1         已在内存中
        48~51       段长度(19:16)           0x0         IA-32e模式下忽略
        52          AVL                     0x0         IA-32e模式下忽略
        53          L                       0x1         64位工作模式，为0表示兼容32位模式
        54          D/B                     0x0         64位工作模式
        55          G                       0x0         IA-32e模式下忽略
        56~63       段基地址(31:24)         0x00        IA-32e模式下忽略
        数据段(Segment 10)解析
        0~15        段长度(15:0)            0x0000      IA-32e模式下忽略
        16~31       段基地址(15:0)          0x0000      IA-32e模式下忽略
        32~39       段基地址(23:16)         0x00        IA-32e模式下忽略
        40~43       Type区域                0x2         向上扩展、可读写、未访问
        44          S标志                   0x1         数据段
        45~46       DPL                     0x00        0特权级
        47          P                       0x1         已在内存中
        48~51       段长度(19:16)           0x0         IA-32e模式下忽略
        52          AVL                     0x0         IA-32e模式下忽略
        53          L                       0x0         IA-32e模式下忽略
        54          D/B                     0x0         IA-32e模式下忽略
        55          G                       0x0         IA-32e模式下忽略
        56~63       段基地址(31:24)         0x00        IA-32e模式下忽略
    */
GDT_Table:
    .quad   0x0000000000000000      /* 0 NULL descriptor    00 */
    .quad   0x0020980000000000      /* 1    KERNEL      Code        64-bit      Segment 08 */
    .quad   0x0000920000000000      /* 2    KERNEL      Data        64-bit      Segment 10 */
    .quad   0x0000000000000000      /* 3    USER        Code        32-bit      Segment 18 */
    .quad   0x0000000000000000      /* 4    USER        Data        32-bit      Segment 20 */
    .quad   0x0020f80000000000      /* 5    USER        Code        64-bit      Segment 28 */
    .quad   0x0000f20000000000      /* 6    USER        Data        64-bit      Segment 30 */
    .quad   0x00cf9a000000ffff      /* 7    KERNEL      Code        32-bit      Segment 38 */
    .quad   0x00cf92000000ffff      /* 8    KERNEL      Data        32-bit      Segment 40 */
    .fill   10,8,0                  /* 10 ~ 11 TSS (jmp one segment <9>) in long-mode 128-bit 50 */
GDT_END:

GDT_POINTER:                        // lgdt写入寄存器的值 
GDT_LIMIT:  .word   GDT_END - GDT_Table - 1
GDT_BASE:   .quad   GDT_Table

// ==========   IDT_Table

.globl IDT_Table

IDT_Table:                          // lidt写入寄存器的值，中断表后续添加，先预留内存空间
    .fill   512,8,0                 // 512个数据，每个数据8字节，数据值为0
IDT_END:

IDT_POINTER:
IDT_LIMIT:  .word   IDT_END - IDT_Table - 1
IDT_BASE:   .quad   IDT_Table

// ==========   TSS64_Table

.globl TSS64_Table

TSS64_Table:                        // TSS表，预留空间
    .fill 13,8,0                    // 13个数据，每个数据8字节，数据值为0
TSS64_END:

TSS64_POINTER:
TSS64_LIMIT:    .word   TSS64_END - TSS64_Table - 1
TSS64_BASE:     .quad   TSS64_Table
